package sql

import (
	"context"
	"fmt"
	"github.com/rs/zerolog/log"
	"gomod.alauda.cn/mysql-mgr-operator/pkg/errorx"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	"satellite/app/option"
	"satellite/pkg/mgrclient"
	remoting "satellite/pkg/remoting"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"strings"
)

type Option struct {
	option.Base
	Cmd      string
	Ordinal  []int
	Password string
}

type Sql struct {
	*Option
	targetMap map[string]bool
}

func NewSql(opt *Option) *Sql {
	target := make(map[string]bool)
	if opt.Ordinal != nil {
		for _, ordinal := range opt.Ordinal {
			name := fmt.Sprintf("%v-%v", opt.Name, ordinal)
			target[name] = true
		}
	}
	return &Sql{
		Option:    opt,
		targetMap: target,
	}
}

func (s *Sql) Run() error {
	err := s.initPassword()
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
	defer cancel()
	items := new(corev1.PodList)
	labels := client.MatchingLabels{
		"v1alpha1.mysql.middleware.alauda.io/cluster": s.Name,
		"app.kubernetes.io/part-of":                   "mysql-mgr-operator",
	}
	err = mgrclient.Client().List(ctx, items, labels)
	if err != nil {
		return err
	}
	if len(items.Items) == 0 {
		return apierrors.NewNotFound(corev1.Resource("pods"), s.Name)
	}
	for _, instant := range items.Items {
		if s.Ordinal == nil || s.targetMap[instant.Name] {
			s.exec(instant.Name)
		}
	}
	return nil
}

func (s *Sql) exec(name string) {
	exec, err := remoting.NewRemoteExec(s.Namespace, "pods", name, "mysql-agent")
	if err != nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
	defer cancel()
	cmd := s.buildMysqlCmd(s.Cmd)
	stdio, stderr, err := exec.Exec(ctx, cmd)
	if err != nil {
		return
	}
	if stderr.String() != "" {
		log.Warn().Str("pod", name).Msg(stderr.String())
	}
	log.Info().Str("pod", name).Msg(stdio.String())
}

func (s *Sql) buildMysqlCmd(cmd string) []string {
	prefix := fmt.Sprintf("mysqlsh root@localhost -p%v --sql -e", s.Password)
	return append(strings.Split(prefix, " "), cmd)
}

func (s *Sql) initPassword() error {
	if s.Password == "" {
		ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
		defer cancel()
		nn := types.NamespacedName{
			Namespace: s.Namespace,
			Name:      s.Name + "-root-internal-password",
		}
		secret := new(corev1.Secret)
		err := mgrclient.Client().Get(ctx, nn, secret)
		if err != nil {
			return err
		}
		if password, ok := secret.Data["password"]; ok {
			s.Password = string(password)
			return nil
		}
		return errorx.PasswordFieldNotFoundErr
	}
	return nil
}
